package com.aboverock.module.system.service.impl;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.aboverock.module.system.constant.SystemConstants;
import com.aboverock.module.system.domain.Dictionary;
import com.aboverock.module.system.mapper.DictionaryMapper;
import com.aboverock.module.system.service.DictionaryService;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Rock Wang
 * @since 2018-08-09
 */
@Service
public class DictionaryServiceImpl extends ServiceImpl<DictionaryMapper, Dictionary>
        implements DictionaryService {

    private static final String PATH_CONNECTOR = SystemConstants.DICTIONARY_PATH_CONNECTOR;

    @Override
    public boolean isDictionaryType(Dictionary dictionary) {
        return null == dictionary.getParentId();
    }

    @Override
    public String getDictionaryTypeCode(Dictionary dictionary) {
        return StringUtils.substringBefore(dictionary.getPath(), PATH_CONNECTOR);
    }

    @Override
    public String getParentDictionaryPath(Dictionary dictionary) {
        return StringUtils.substringBeforeLast(dictionary.getPath(), PATH_CONNECTOR);
    }

    @Override
    public String generateDictionaryPath(Dictionary dictionary) {
        if (this.isDictionaryType(dictionary)) {
            return dictionary.getCode();
        }

        Dictionary dic = this.getById(dictionary.getParentId());
        return dic.getPath() + PATH_CONNECTOR + dictionary.getCode();
    }

    @Override
    public boolean isDuplicatedCode(Dictionary dictionary) {
        if (StringUtils.isBlank(dictionary.getPath())) {
            dictionary.setPath(generateDictionaryPath(dictionary));
        }
        QueryWrapper<Dictionary> query = new QueryWrapper<Dictionary>().eq("path",
                dictionary.getPath());

        // 如果更新，则排除当前字典的code，判断与其余同级别是否重复
        if (dictionary.getId() != null) {
            query.ne("id", dictionary.getId());
        }

        Dictionary dic = this.getOne(query);
        return (null == dic) ? false : true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(Dictionary dictionary, Dictionary oldDictionary) throws SQLException {
        if (dictionary.getCode().equalsIgnoreCase(oldDictionary.getCode())) {
            return updateById(dictionary);
        } else {
            StringBuilder replace = new StringBuilder("`path`=replace(`path`, '")
                    .append(oldDictionary.getPath()).append(PATH_CONNECTOR).append("', '")
                    .append(dictionary.getPath()).append(PATH_CONNECTOR).append("')");
            UpdateWrapper<Dictionary> update = new UpdateWrapper<Dictionary>().setSql(replace.toString());
            if (updateById(dictionary) && this.update(new Dictionary(), update)) {
                return true;
            } else {
                throw new SQLException("update dictionary fail");
            }
        }
    }

    @Override
    public boolean remove(Dictionary dictionary) {
        return this.remove(
                new QueryWrapper<Dictionary>().and(query -> query.eq("id", dictionary.getId()).or()
                        .likeRight("path", dictionary.getPath() + PATH_CONNECTOR)));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean exchangeOrderNo(Dictionary dictionary1, Dictionary dictionary2)
            throws SQLException {
        Long orderNo1 = dictionary1.getOrderNo();
        Long orderNo2 = dictionary2.getOrderNo();
        dictionary1.setOrderNo(orderNo2);
        dictionary2.setOrderNo(orderNo1);

        if (updateById(dictionary1) && updateById(dictionary2)) {
            return true;
        } else {
            throw new SQLException("exchange dictionary orderNo fail");
        }
    }

    @Override
    public Dictionary buildTree(List<Dictionary> dictionaries) {
        if (CollectionUtils.isEmpty(dictionaries)) {
            return null;
        }

        dictionaries.sort((d1, d2) -> d1.getPath().compareTo(d2.getPath()));

        Dictionary result = dictionaries.get(0);
        for (int i = 1; i < dictionaries.size(); i++) {
            int index = Collections.binarySearch(dictionaries, dictionaries.get(i), (destination,
                    source) -> destination.getPath().compareTo(getParentDictionaryPath(source)));
            if (null == dictionaries.get(index).getChildren()) {
                dictionaries.get(index).setChildren(
                        new TreeSet<Dictionary>(Comparator.comparing(Dictionary::getOrderNo)));
            }
            dictionaries.get(index).getChildren().add(dictionaries.get(i));
        }

        return result;
    }

    @Override
    public List<Dictionary> getAllDictionaryType() {
        return this.list(new QueryWrapper<Dictionary>().isNull("parent_id"));
    }

    @Override
    public Dictionary getDictionaryTypeByCode(String typeCode) {
        return this.getOne(new QueryWrapper<Dictionary>().isNull("parent_id").eq("code", typeCode));
    }

    @Override
    public Dictionary getDictionaryTreeByCodes(String... codes) {
        if (0 == codes.length) {
            return null;
        }

        StringBuilder codePath = new StringBuilder();
        for (int i = 0; i < codes.length - 1; i++) {
            codePath.append(codes[i]).append(PATH_CONNECTOR);
        }
        codePath.append(codes[codes.length - 1]);

        List<Dictionary> list = list(new QueryWrapper<Dictionary>().and(query -> query
                .eq("path", codePath.toString()).or()
                .likeRight("path", codePath.toString() + PATH_CONNECTOR)));

        return buildTree(list);
    }

    @Override
    public List<Dictionary> getDictionariesByTypeCode(String typeCode) {
        Dictionary dictionaryType = getDictionaryTypeByCode(typeCode);
        if (dictionaryType != null) {
            return getDictionariesByParentId(dictionaryType.getId());
        }

        return Collections.emptyList();
    }

    @Override
    public List<Dictionary> getDictionariesByParentId(String parentId) {
        return this.list(
                new QueryWrapper<Dictionary>().eq("parent_id", parentId).orderByAsc("order_no"));
    }

    @Override
    @Deprecated
    public List<Dictionary> getAllDictionariesByTypeCode(String typeCode) {
        List<Dictionary> dictionaries = new ArrayList<>();

        Dictionary dictionaryType = getDictionaryTypeByCode(typeCode);
        if (null == dictionaryType) {
            return dictionaries;
        }

        dictionaries.add(dictionaryType);
        Set<String> ids = new HashSet<>();
        ids.add(dictionaryType.getId());
        while (!ids.isEmpty()) {
            List<Dictionary> children = this
                    .list(new QueryWrapper<Dictionary>().in("parent_id", ids));
            dictionaries.addAll(children);

            ids.clear();
            ids.addAll(children.stream().map(Dictionary::getId).collect(Collectors.toSet()));
        }

        return dictionaries;
    }
}
